#include "andrea.mugnaini.h"
#include "../common/basetypes.h"
#include "../common/MathUtils.h"
#include "../common/ProblemCommons.h"
#include "MathManager.h"
#include <math.h>
#include <sstream>
#include <iostream>
bool AndreaMugnaini::Init(Register& o_register)
{
	m_name = "ANDREA MUGNAINI";
	
	REGISTER_FUNC(AndreaMugnaini, 1);
	REGISTER_FUNC(AndreaMugnaini, 2);
	REGISTER_FUNC(AndreaMugnaini, 3);
	REGISTER_FUNC(AndreaMugnaini, 4);
	REGISTER_FUNC(AndreaMugnaini, 5);
	REGISTER_FUNC(AndreaMugnaini, 6);
	REGISTER_FUNC(AndreaMugnaini, 7);
	REGISTER_FUNC(AndreaMugnaini, 8);
	REGISTER_FUNC(AndreaMugnaini, 9);
	REGISTER_FUNC(AndreaMugnaini, 10);
	REGISTER_FUNC(AndreaMugnaini, 11);
	//REGISTER_FUNC(AndreaMugnaini, 12);
	REGISTER_FUNC(AndreaMugnaini, 28);
	REGISTER_FUNC(AndreaMugnaini, 30);

	return true;
}
/*
* Super optimized solution!
*/
RetType AndreaMugnaini::Problem1()
{
	int result = 0;
	int dividend = 1;
	for (; dividend < 67; ++dividend)
	{
		result = result + dividend*5 + dividend*3 - dividend*15;
	}
	for (; dividend < 200; ++dividend)
	{
		result = result + dividend*5 + dividend*3;
	}
	for (; dividend < 334; ++dividend)
	{
		result = result + dividend*3;
	}
	return static_cast<RetType>(result);
}

RetType AndreaMugnaini::Problem2()
{
	int counter = 0;
	int fibonacci_0 = 1;
	int fibonacci_1 = 2;
	while (fibonacci_1 < 4000000)
	{
		int fibonacci_temp = fibonacci_0 + fibonacci_1;
		fibonacci_0 = fibonacci_1;
		fibonacci_1 = fibonacci_temp;
		counter += MathManager::isDivisible(fibonacci_0, 2) ? fibonacci_0 : 0;
	}
	return counter;
}

RetType AndreaMugnaini::Problem3()
{	
	RetType number = 600851475143;
	int factor = 2;
	int maxFactor = 0;	
	while (number != 1)
	{		
		if (number % factor == 0)
		{
			number /= factor;
		}
		else 
		{
			++factor;
			maxFactor = factor > maxFactor && math::IsPrime(factor) ? factor : maxFactor;
		}		
	}	
	return maxFactor;
}
	
RetType AndreaMugnaini::Problem4()
{	
	int number_1 = 999, number_1_init = 999;
	int number_2 = 999, number_2_init = 999;
	int maxPalindrome = 0;
	int possiblePalindrome;
	do
	{
		do
		{
			possiblePalindrome = number_1 * number_2;
			maxPalindrome = maxPalindrome < possiblePalindrome && math::IsPalindrome(possiblePalindrome) ? possiblePalindrome : maxPalindrome;
			--number_1;
		} while (number_1 >= 100);		
		number_1 = --number_1_init;
		number_2 = --number_2_init;
	} while (number_2 >= 100);	
	return maxPalindrome;
}

RetType AndreaMugnaini::Problem5()
{
	int mcmResult = 1;
	for (int i = 1;i <= 20;i++)
		mcmResult = math::GetMCM(mcmResult, i);
	return mcmResult;
}

RetType AndreaMugnaini::Problem6()
{
	int sumOfSquares = 0;
	int squareOfSums = 0;
	for (int i = 0;i < 101;++i)
	{
		sumOfSquares += math::IntPow(i, 2);
		squareOfSums +=i;
	}
	squareOfSums = math::IntPow(squareOfSums, 2);
	return static_cast<RetType>(squareOfSums - sumOfSquares);
}

/*
* Ok, the best algorithms to find prime numbers are Sieve of Atkin and Tiny Fermat Theorem
* ...
* But, I choose the brute force algorithm because it's long only five lines of code, simple and compact!
*
*/
RetType AndreaMugnaini::Problem7()
{
	int cardinalitaNumeriPrimi = 1;
	int number = 1;
	while (cardinalitaNumeriPrimi < 10001)
	{		
		number += 2;
		cardinalitaNumeriPrimi += math::IsPrime(number) ? 1 : 0;		
	}
	return number;
}

/*
* I must scan the buffer taking a sub-buffer of four elements at once,
* to find the sub-set with the greater sum of the inner elements.
*/
RetType AndreaMugnaini::Problem8()
{
	char* buffer = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450";	
	int indexOfFirstDigit = 0;
	int maxSum = 0;
	int tempSum = 0;
	for (int i = 0;i < 1000;++i)
	{
		for (int j = 0;j < 5;++j)
		{
			tempSum += buffer[i+j];
		}
		if (tempSum > maxSum)
		{
			indexOfFirstDigit = i;
			maxSum = tempSum;
		}
		tempSum = 0;
	}
	return MathManager::asciiToInt(buffer[indexOfFirstDigit + 0]) * MathManager::asciiToInt(buffer[indexOfFirstDigit + 1]) * MathManager::asciiToInt(buffer[indexOfFirstDigit + 2]) * MathManager::asciiToInt(buffer[indexOfFirstDigit + 3]) * MathManager::asciiToInt(buffer[indexOfFirstDigit + 4]);
}

/*
* I solved this problem in 2 different ways, 
* the second way (i commented) make use of  the integer square root of a positive number of any type thanks to template
*/
RetType AndreaMugnaini::Problem9()
{		
	int a,b,c = 1000;
	for (a = 200;a < 1000;++a)
	{
		for (b = a + 1;b < 1000;++b)
		{
			for (c = b + 1;a+b+c < 1000;++c);
			if (MathManager::my_pow(a, 2) + MathManager::my_pow(b, 2) == MathManager::my_pow(c,2))
			{
				if ((a+b+c)==1000)
				{
					return a * b * c;
				}
			}
		}
	}
	return -1;
		
	/*
	for (a = 200;a < 1000;++a)
	{
		for (b = a + 1;b < 1000;++b)
		{
			int temp = MathManager::my_pow(a, 2)+MathManager::my_pow(b, 2);
			c = MathManager::intSqrt(temp);
			if (MathManager::my_pow(c,2) == temp)
			{			
				if ((a+b+c)==1000) return a*b*c;
			}
		}
	}
	*/
}

RetType AndreaMugnaini::Problem10()
{
	RetType number = 17;
	for (UInt i = 11;i < 2000000;i += 2)
	{
		if (math::IsPrime(i)) 
			number += i;
	}
	return number;
}

/*
* My solution is very easy to understand and fast :)
* but to find it..., I admit it, I spent a whole morning :(
*/
RetType AndreaMugnaini::Problem11()
{
	const int* const grid = problem_data::problem11numbers;
	RetType maxProduct = 1;
	RetType tempProduct = 1;

	for (int i = 0;i < 397;++i)
	{
		// search right
		if (i <= 16)
		{
			for (int right = 0;right < 4;++right)
			{
				tempProduct *= *(grid + i + right);
			}
			maxProduct = tempProduct > maxProduct ? tempProduct : maxProduct;
			tempProduct = 1;
		}
		// search downRight
		if (i <= 336)
		{
			for (int downRight = 0;downRight < 4;++downRight)
			{
				tempProduct *= *(grid + i + downRight*20 + downRight);
			}
			maxProduct = tempProduct > maxProduct ? tempProduct : maxProduct;
			tempProduct = 1;
		}		
		// search down and downLeft
		if (i <= 339)
		{
			for (int down = 0;down < 4;++down)
			{
				tempProduct *= *(grid + i + down*20);
			}
			maxProduct = tempProduct > maxProduct ? tempProduct : maxProduct;
			tempProduct = 1;

			for (int downLeft = 0;downLeft < 4;++downLeft)
			{
				tempProduct *= *(grid + i + downLeft*20 - downLeft);
			}
			maxProduct = tempProduct > maxProduct ? tempProduct : maxProduct;
			tempProduct = 1;
		}		
	}
	return maxProduct;
}

RetType AndreaMugnaini::Problem12()
{

	return 0;
}
/*
* I used an algorithm based on this observation(assuming, es: d(4) => distance=4):
* 21----------d(4)----------25
* |                          |
* |      7 -----------9      |
* |      |            |      |
* d(4)   d(2)   1    d(2)  d(4)
* |      |            |      |
  |      5------------3      |
* |                          |
* |                          |
* 17----------d(4)----------13

*/
RetType AndreaMugnaini::Problem28()
{
	int sum = 1;
	int number = 1;
	int cardinalityElementsOnDiagonals = 2002;
	int fattoreDiIncremento = 2;
	int jumpCounter = 0;

	for (int i = 2;i < cardinalityElementsOnDiagonals;++i)
	{				
		number += fattoreDiIncremento;
		sum += number;
		++jumpCounter;
		if (jumpCounter==4)
		{
			jumpCounter = 0;
			fattoreDiIncremento += 2;
		}
	}
	return static_cast<RetType>(sum);
}

/*
* Ok, i must solve "narcissistic number" problem.
*
* this is my solution: 240559, but on the project euler web site appears to be incorrect...
* Hence, i tried to search some informations and i have found this resource:
* http://mathworld.wolfram.com/NarcissisticNumber.html
* ...
* with the informations i have available, my conclusion is that my result is correct.
* Anyway, I will search more knowledge.
*/
RetType AndreaMugnaini::Problem30()
{	
	int sum = 0;
	int numberCandidate = 0;

	int powOfDigits[10];
	for (int i = 0;i < 10;++i)
	{
		powOfDigits[i] = MathManager::my_pow(i, 5);
	}

	for (int number = 54748;number <= 100000;++number)
	{					
		std::stringstream ss;
		ss << number;		
		std::string stringTemp = ss.str();		
		for (int i = 0;i < 5;++i)
		{
			numberCandidate += powOfDigits[MathManager::asciiToInt(stringTemp.at(i))];
		}

		if (number==numberCandidate)
		{
			sum += number;
		}		
		numberCandidate = 0;
	}
	return static_cast<RetType>(sum);
}